package com.kamistoat.meimeistore.product.thread;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// 测试
public class CompletableFutureTest {
    // 建立一个大小为10的固定大小线程池
    public static ExecutorService servicePool = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        // 使用Runnable接口初始化CompletableFuture

        CompletableFuture<Void> runAsync01 = CompletableFuture.runAsync(() -> {
            // 直接用函数式接口。因为Runnable接口只有一个run方法要覆写，这个run方法还是空输入+空返回值
            System.out.println("runAsync01当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("Runnable:" + i);
        }, servicePool);

        CompletableFuture<Integer> supplyAsync01 = CompletableFuture.supplyAsync(() -> {
            // 直接用函数式接口。因为Supplier接口只有一个get方法要覆写，这个get方法是空输入+有返回值
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 0;
            System.out.println("Supplier:" + i);
            return i;
        }, servicePool).whenComplete((result, exception) -> {
            // 链式调用.whenCompletable()，对刚刚完成的异步任务执行回调
            // .whenCompletable方法只有一个accept(r,e)方法要覆写，输入参数r自动映射链式异步任务的结果，e映射链式异步任务的异常
            System.out.println("回调-异步任务结果：" + result);
            System.out.println("回调-异步任务异常：" + exception);
        }).exceptionally(exception -> {
            // 链式调用.exceptionally()，对刚刚完成的异步任务执行异常捕获
            // 只有换一个r=apply(e)要覆写，输入参数e自动映射链式异步任务的异常，输入参数r映射链式异步任务的结果
            System.out.println("异常捕获-异步任务异常");
            // 这个return将会给链式异步任务的返回值赋值，覆盖掉因异常而错误的返回值
            return 10;
        });
        // CompletableFuture的返回值，就是内部异步方法的返回值
        // 但是同样，一旦想要获取返回值，main方法一定会等待异步任务全部执行完成后再结束
        // 此时调用supplyAsync.get()可以看到值是.exceptionally()中的新值
        System.out.println(supplyAsync01.get());

        CompletableFuture<Integer> supplyAsync02 = CompletableFuture.supplyAsync(() -> {
            // 直接用函数式接口。因为Supplier接口只有一个get方法要覆写，这个get方法是空输入+有返回值
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 100 / 5;
            System.out.println("Supplier:" + i);
            return i;
        }, servicePool).handle((result, exception) -> {
            // 使用handle，无论链式异步是否成功，都能对异步结果进行处理
            System.out.println("handle-异步任务结果：" + result);
            System.out.println("handle-异步任务异常：" + exception);
            if (exception == null) {
                return result * 2;
            } else {
                return 10;
            }
        });
        System.out.println(supplyAsync02.get());

        // 使用.thenRun来串行。注意，由于.thenRun是没有返回值的，所以虽然第一个异步任务有返回值i，但整个CompletableFuture的返回值是void
        CompletableFuture<Void> supplyAsync03 = CompletableFuture.supplyAsync(() -> {
            // 直接用函数式接口。因为Supplier接口只有一个get方法要覆写，这个get方法是空输入+有返回值
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 100 / 5;
            System.out.println("Supplier:" + i);
            return i;
        }, servicePool).thenRunAsync(() -> {
            System.out.println("thenRun线程串行");
        }, servicePool);

        // 使用.thenAccept来串行。注意，由于.thenAccept是没有返回值的，所以虽然第一个异步任务有返回值i，但整个CompletableFuture的返回值是void
        CompletableFuture<Void> supplyAsync04 = CompletableFuture.supplyAsync(() -> {
            // 直接用函数式接口。因为Supplier接口只有一个get方法要覆写，这个get方法是空输入+有返回值
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 100 / 5;
            System.out.println("Supplier:" + i);
            return i;
        }, servicePool).thenAcceptAsync((result) -> {
            System.out.println("thenAccept线程串行" + result);
        }, servicePool);


        // 使用.thenApply来串行。注意整个CompletableFuture<U> 的U和最后一个串行线程返回值数据类型相同
        CompletableFuture<String> supplyAsync05 = CompletableFuture.supplyAsync(() -> {
            // 直接用函数式接口。因为Supplier接口只有一个get方法要覆写，这个get方法是空输入+有返回值
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 100 / 5;
            System.out.println("Supplier:" + i);
            return i;
        }, servicePool).thenApplyAsync((result) -> {
            System.out.println("thenApply线程串行" + result);
            return "thenApply新异步任务的结果xxxxxx";
        }, servicePool);
        System.out.println(supplyAsync05.get());

        // 使用.thenCombineAsync相与组合异步任务
        CompletableFuture<Integer> supplyAsync06 = CompletableFuture.supplyAsync(() -> {
            System.out.println("相与组合任务1");
            // 直接用函数式接口。因为Supplier接口只有一个get方法要覆写，这个get方法是空输入+有返回值
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 100 / 5;
            System.out.println("Supplier:" + i);
            return i;
        }, servicePool);
        CompletableFuture<String> supplyAsync07 = CompletableFuture.supplyAsync(()->{
            System.out.println("相与组合任务2");
            System.out.println("当前线程：" + Thread.currentThread().getId());
            return "hello";
        },servicePool);
        CompletableFuture<String> thenCombineAsync = supplyAsync06.thenCombineAsync(supplyAsync07, (result1, result2) -> {
            System.out.println("相与组合任务结束后执行");
            System.out.println("当前线程：" + Thread.currentThread().getId());
            return result2 + result1;
        }, servicePool);
        System.out.println(thenCombineAsync.get());

        // 使用.applyToEitherAsync相与组合异步任务
        CompletableFuture<Object> supplyAsync08 = CompletableFuture.supplyAsync(() -> {
            System.out.println("或组合任务1");
            // 直接用函数式接口。因为Supplier接口只有一个get方法要覆写，这个get方法是空输入+有返回值
            System.out.println("当前线程：" + Thread.currentThread().getId());
//            try {
//                Thread.sleep(1000);
//                System.out.println("或组合任务1睡醒");
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
            int i = 100 / 0;
            System.out.println("Supplier:" + i);
            return i;
        }, servicePool);
        CompletableFuture<Object> supplyAsync09 = CompletableFuture.supplyAsync(()->{
            System.out.println("或组合任务2");
            System.out.println("当前线程：" + Thread.currentThread().getId());
            return "hello";
        },servicePool);
        CompletableFuture<String> applyToEitherAsync = supplyAsync08.applyToEitherAsync(supplyAsync09, (result) -> {
            System.out.println("或任务成功者结果：" + result);
            return result.toString();
        }, servicePool);
        System.out.println(applyToEitherAsync.get());


        System.out.println("main...end...");
    }
}
